﻿namespace Domains
{
    using System;
    using System.IO;
    using System.Text.RegularExpressions;
    using System.Xml;
    using System.Collections.Generic;
    using System.Diagnostics;
    using HtmlAgilityPack;
    using System.Threading;
    using System.Web;

    /// <summary>
    /// Information about merged files
    /// </summary>
    public static class ComparedFiles
    {
        #region Public Methods

        /// <summary>
        /// The result of comparation is the table which contain two files.
        /// So, this method cuts this table into two files
        /// </summary>
        /// <param name="ComparationName">The result of comporation</param>
        /// <returns>Two strings with html content</returns>
        public static string[] DivideComparationResult(string ComparationName)
        {
            HtmlAgilityPack.HtmlDocument ComparationDocument = new HtmlAgilityPack.HtmlDocument();
            Execution.ReadFile(ComparationName);
            ComparationDocument.Load(ComparationName);

            foreach (HtmlNode item in ComparationDocument.DocumentNode.SelectNodes("/html/head/style"))
            {
                item.InnerHtml = item.InnerHtml.Replace("td { vertical-align: top; padding-left: 4px; padding-right: 4px; }", "td { vertical-align: top; padding-left: 4px; padding-right: 4px; white-space: pre; }");
                item.InnerHtml = item.InnerHtml.Replace("{ font-family: sans-serif; font-size: 11pt; }", "{ font-family: sans-serif; font-size: 9pt; }");
                item.InnerHtml = item.InnerHtml.Replace("td.TextItemNum { color: #827357; background-color: #F2F2F2; }", "td.TextItemNum { color: #827357; background-color: #F2F2F2; border-right: 1px solid Black;}");
                item.InnerHtml = item.InnerHtml + "tr.Selection td { border-left: none; border-top: 1px solid Gray; border-bottom: 1px solid Gray; border-right: 1px solid Black; color: #000000; background-color: #CDFFFF; }" + "\n";
                item.InnerHtml = item.InnerHtml.Replace("tr.SectionBegin td { border-left: none; border-top: none; border-right: 1px solid Black; }", "tr.SectionSelection td { border-left: none; border-top: 1px solid Black; border-bottom: 1px solid Black; border-right: 1px solid Black; }");
            }

            ComparationDocument.DocumentNode.SelectSingleNode("/html/head").AppendChild(ComparationDocument.CreateElement("script"));
            HtmlNode node = ComparationDocument.DocumentNode.SelectSingleNode("/html/head/script");
            StreamReader Reader = new StreamReader(AppDomain.CurrentDomain.BaseDirectory + "Scripts\\Coloredlines.js");
            HtmlNode TextNode = ComparationDocument.CreateTextNode(Reader.ReadToEnd());
            node.AppendChild(TextNode);
            Reader.Close();


            HtmlDocument LeftDocument = new HtmlDocument();
            HtmlDocument RightDocument = new HtmlDocument();
            RightDocument.DocumentNode.AppendChild(RightDocument.CreateElement("html"));
            LeftDocument.DocumentNode.AppendChild(LeftDocument.CreateElement("html"));

            LeftDocument.DocumentNode.SelectSingleNode("/html").AppendChild(ComparationDocument.DocumentNode.SelectSingleNode("/html/head").Clone());
            RightDocument.DocumentNode.SelectSingleNode("/html").AppendChild(ComparationDocument.DocumentNode.SelectSingleNode("/html/head").Clone());
            LeftDocument.DocumentNode.SelectSingleNode("/html").AppendChild(LeftDocument.CreateElement("body"));
            RightDocument.DocumentNode.SelectSingleNode("/html").AppendChild(RightDocument.CreateElement("body"));
            LeftDocument.DocumentNode.SelectSingleNode("/html/body").AppendChild(LeftDocument.CreateElement("table"));
            RightDocument.DocumentNode.SelectSingleNode("/html/body").AppendChild(RightDocument.CreateElement("table"));

            int Count = default(int);
            foreach (HtmlNode item in ComparationDocument.DocumentNode.SelectNodes("/html/body/table/tr"))
            {
                Count++;
                HtmlNode NewLeftNode = LeftDocument.DocumentNode.SelectSingleNode("/html/body/table").AppendChild(LeftDocument.CreateElement("tr"));
                HtmlNode NewRightNode = RightDocument.DocumentNode.SelectSingleNode("/html/body/table").AppendChild(RightDocument.CreateElement("tr"));
                foreach (HtmlAttribute attr in item.Attributes)
                {
                    NewLeftNode.SetAttributeValue(attr.Name, attr.Value);
                    NewRightNode.SetAttributeValue(attr.Name, attr.Value);
                }
            }

            foreach (HtmlNode item in ComparationDocument.DocumentNode.SelectNodes("/html/body/table/tr"))
            {
                int ChildNodeNumber = default(int);
                foreach (HtmlNode ChildNode in item.ChildNodes)
                {
                    if ((ChildNodeNumber == 1) || (ChildNodeNumber == 3))
                    {
                        foreach (HtmlNode LeftNode in LeftDocument.DocumentNode.SelectNodes("/html/body/table/tr"))
                        {
                            if (LeftNode.ChildNodes.Count != 2)
                            {
                                LeftNode.AppendChild(ChildNode.Clone());
                                break;
                            }
                        }
                    }
                    if ((ChildNodeNumber == 7) || (ChildNodeNumber == 9))
                    {
                        foreach (HtmlNode RightNode in RightDocument.DocumentNode.SelectNodes("/html/body/table/tr"))
                        {
                            if (RightNode.ChildNodes.Count != 2)
                            {
                                RightNode.AppendChild(ChildNode.Clone());
                                break;
                            }
                        }
                    }
                    ChildNodeNumber++;
                }
            }

            string[] ComparationResults = new string[2];
            ComparationResults[0] = Execution.DocumentToString(LeftDocument);
            ComparationResults[1] = Execution.DocumentToString(RightDocument);
            return ComparationResults;
        }

        /// <summary>
        /// Merging left and right files
        /// </summary>
        /// <param name="LeftHtmlInString">Left side - string with html content</param>
        /// <param name="RightHtmlInString">Right side - string with html content</param>
        /// <param name="Position">Position of file (previus, custom, next)</param>
        public static void Merge(string LeftHtmlInString, string RightHtmlInString, string Position)
        {
            if (Position == "Customer")
            {
                string EmptyLinesAdded = AddEmptyLines(LeftHtmlInString, RightHtmlInString);
                string SelectionMerged = MergingSelection(EmptyLinesAdded, RightHtmlInString);
                Customer = AddIdAndAllocationToTrTag(SelectionMerged); 
            }

            if (Position == "Previus")
            {
                string EmptyLinesAdded = AddEmptyLines(LeftHtmlInString, RightHtmlInString);
                Previus = AddIdAndAllocationToTrTag(EmptyLinesAdded);
            }

            if (Position == "Next")
            {
                string EmptyLinesAdded = AddEmptyLines(LeftHtmlInString, RightHtmlInString);
                Next = AddIdAndAllocationToTrTag(EmptyLinesAdded);
            }
        }

        #endregion

        /// <summary>
        /// Adding empty lines to file
        /// </summary>
        /// <param name="left">left file in compare</param>
        /// <param name="right">right file in compare</param>
        public static string AddEmptyLines(string left, string right)
        {
            List<HtmlNode> LeftNodesCollection = new List<HtmlNode>();
            List<HtmlNode> RightNodesCollection = new List<HtmlNode>();
            List<HtmlNode> NodesCollection = new List<HtmlNode>();

            HtmlDocument LeftDoc = new HtmlDocument();
            LeftDoc.LoadHtml(left);
            HtmlDocument RightDoc = new HtmlDocument();
            RightDoc.LoadHtml(right);

            foreach (HtmlNode item in LeftDoc.DocumentNode.SelectNodes("/html/body/table/tr"))
            {
                LeftNodesCollection.Add(item.Clone());
                item.Remove();
            }

            foreach (HtmlNode item in RightDoc.DocumentNode.SelectNodes("/html/body/table/tr"))
            {
                RightNodesCollection.Add(item.Clone());
            }

            int Count = default(int);
            foreach (HtmlNode item in LeftNodesCollection)
            {
                if (item.ChildNodes[0].InnerText != RightNodesCollection[Count].ChildNodes[0].InnerText)
                {
                    if (item.ChildNodes[0].InnerText == "&nbsp;")
                    {
                        NodesCollection.Add(item.Clone());
                    }
                    else
                    {
                        while (RightNodesCollection[Count].ChildNodes[0].InnerText == "&nbsp;")
                        {
                            NodesCollection.Add(RightNodesCollection[Count].Clone());
                            Count++;
                        }
                        NodesCollection.Add(item.Clone());
                        Count++;
                    }
                }
                else
                {
                    NodesCollection.Add(item.Clone());
                    Count++;
                }
            }

            foreach (HtmlNode item in LeftDoc.DocumentNode.SelectNodes("/html/body/table"))
            {
                foreach (HtmlNode node in NodesCollection)
                {
                    item.ChildNodes.Add(node.Clone());
                }
            }

            if (NodesCount == 0)
            {
                NodesCount = Count;
            }

            return Execution.DocumentToString(LeftDoc);
        }

        /// <summary>
        /// Adding Id and Allocation class to "tr" tag in Html file wich represents by string
        /// </summary>
        /// <param name="HtmlString">String with html content</param>
        /// <returns>Processed string with html content (classes for id and allocation have been added)</returns>
        private static string AddIdAndAllocationToTrTag(string HtmlString)
        {
            HtmlDocument Doc = new HtmlDocument();
            Doc.LoadHtml(HtmlString);

            int Count = default(int);
            foreach (HtmlNode item in Doc.DocumentNode.SelectNodes("/html/body/table/tr"))
            {
                if (item.ChildNodes[1].Attributes[0].Value == "TextItemSigDiffMod")
                {
                    item.Attributes[0].Value = "SectionSelection";
                }
                else
                {
                    item.Attributes[0].Value = "SectionMiddle";
                }
                if (item.ChildNodes[0].InnerText != "&nbsp;")
                {
                    Count++;
                    item.Attributes.Add("Id", Convert.ToString(Count));
                }
            }

            return Execution.DocumentToString(Doc);
        }

        /// <summary>
        /// Merging left and right files concerning selection
        /// </summary>
        /// <param name="left">left file in string</param>
        /// <param name="right">right file in string</param>
        public static string MergingSelection(string left, string right)
        {
            HtmlDocument LeftDoc = new HtmlDocument();
            LeftDoc.LoadHtml(left);

            foreach (HtmlNode item in LeftDoc.DocumentNode.SelectNodes("/html/body/table/tr"))
            {
                foreach (HtmlNode TdItem in item.ChildNodes)
                {
                    if (TdItem.NextSibling == null)
                    {
                        HtmlNode RightNode = FindColored(right, TdItem);
                        if (RightNode != null)
                        {
                            int IsChanged = 0;

                            foreach (HtmlAttribute ChildAttr in TdItem.Attributes)
                            {
                                if (ChildAttr.Value == "TextItemSame")
                                {
                                    ChildAttr.Value = "TextItemSigDiffMod";
                                    TdItem.InnerHtml = RightNode.InnerHtml;
                                    IsChanged = 1;
                                }
                            }
                            if (IsChanged == 0)
                            {
                                TdItem.InnerHtml = SelectionGluing(TdItem.InnerHtml, RightNode.InnerHtml, TdItem.InnerText);
                            }
                        }
                    }
                }
            }

            return Execution.DocumentToString(LeftDoc);
        }

        /// <summary>
        /// Find colored line in right file for specified line (node) from left file
        /// </summary>
        /// <param name="right">html content of file in string</param>
        /// <param name="LeftNode">The node from left file</param>
        /// <returns>Null if specified line not color, HtmlNode for colored line</returns>
        private static HtmlNode FindColored(string right, HtmlNode LeftNode)
        {
            HtmlDocument RightDoc = new HtmlDocument();
            RightDoc.LoadHtml(right);

            List<string> ColoredValues = new List<string>();

            foreach (HtmlNode item in RightDoc.DocumentNode.SelectNodes("/html/body/table/tr"))
            {
                foreach (HtmlNode TdItem in item.ChildNodes)
                {
                    if ((TdItem.NextSibling == null) && (TdItem.InnerText == LeftNode.InnerText) && (TdItem.InnerHtml != LeftNode.InnerHtml))
                    {
                        foreach (HtmlNode ChildItem in TdItem.ChildNodes)
                        {
                            foreach (HtmlAttribute ChildAttr in ChildItem.Attributes)
                            {
                                if (ChildAttr.Value == "TextSegSigDiff")
                                {
                                    return TdItem;
                                }
                            }
                        }
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// Gluing marked lines
        /// </summary>
        /// <param name="LeftHtml">Marked line from left html file</param>
        /// <param name="RightHtml">Marked line from right html file</param>
        /// <param name="InnerHtml">Text from marked line (it equal for both line)</param>
        /// <returns>Marked line which cluing from two files</returns>
        public static string SelectionGluing(string LeftHtml, string RightHtml, string InnerHtml)
        {
            int[] LeftMarks = MarkString(LeftHtml);
            int[] RightMarks = MarkString(RightHtml);

            int[] MergedMarks = MergeMarkStrings(LeftMarks, RightMarks);

            return DecorMarkStrings(MergedMarks, InnerHtml);
        }

        /// <summary>
        /// Marking colored elements
        /// </summary>
        /// <param name="input">Html string whith colored elements that identified by span tag</param>
        /// <returns>Array of integer with "0" for unmarked elements and "1" for marked</returns>
        public static int[] MarkString(string input)
        {
            List<int> Marks = new List<int>();

            string[] substrings = Regex.Split(input, "<span class=\"TextSegSigDiff\">");

            foreach (var item in substrings)
            {
                if (item.IndexOf("</span>") >= 0)
                {
                    string[] subsitem = Regex.Split(item, "</span>");

                    for (int i = 0; i < subsitem[0].Length; i++)
                    {
                        Marks.Add(1);
                    }

                    for (int i = 0; i < subsitem[1].Length; i++)
                    {
                        Marks.Add(0);
                    }
                }
                else
                {
                    for (int i = 0; i < item.Length; i++)
                    {
                        Marks.Add(0);
                    }
                }
            }

            int[] ResultMarks = new int[Marks.Count];

            for (int i = 0; i < ResultMarks.Length; i++)
            {
                ResultMarks[i] = Marks[i];
            }

            return ResultMarks;
        }

        /// <summary>
        /// Merging colored marks of two lines
        /// </summary>
        /// <param name="leftMarks">Marks of colored for left line</param>
        /// <param name="rightMarks">Marks of colored for right line</param>
        /// <returns>Merging colored marks</returns>
        public static int[] MergeMarkStrings(int[] leftMarks, int[] rightMarks)
        {
            int[] ResultMarks = new int[leftMarks.Length];
            for (int i = 0; i < leftMarks.Length; i++)
            {
                if ((leftMarks[i] == 1) | (rightMarks[i] == 1))
                {
                    ResultMarks[i] = 1;
                }
                else
                {
                    ResultMarks[i] = 0;
                }
            }
            return ResultMarks;
        }

        /// <summary>
        /// Decorating strings according to mark symbols in them
        /// </summary>
        /// <param name="MarkStrings">Marks for colored symbols</param>
        /// <param name="HtmlString">String for array with colored symbols</param>
        /// <returns>Decorated string</returns>
        public static string DecorMarkStrings(int[] MarkStrings, string HtmlString)
        {
            string DecorString = default(string);
            string PartOfString = HtmlString[0].ToString();

            for (int i = 1; i < MarkStrings.Length; i++)
            {
                if (MarkStrings[i] == MarkStrings[i - 1])
                {
                    PartOfString = PartOfString + HtmlString[i];
                }
                if ((MarkStrings[i] != MarkStrings[i - 1]) || (i == MarkStrings.Length - 1))
                {
                    DecorString = DecorString + Decorate(MarkStrings[i - 1], PartOfString);
                    PartOfString = HtmlString[i].ToString();
                }
            }

            return DecorString;
        }

        /// <summary>
        /// Decorated string according to colored marks
        /// </summary>
        /// <param name="ColorNumber">The number of color marks</param>
        /// <param name="StringForDecor">The string for wich are color marks</param>
        /// <returns>Decorated string</returns>
        private static string Decorate(int Marker, string StringForDecor)
        {
            string DecoratedString = StringForDecor;
            if (Marker != 0)
            {
                DecoratedString = "<span class=\"TextSegSigDiff\">" + StringForDecor + "</span>";
            }
            return DecoratedString;
        }

        #region Properties

        /// <summary>
        /// Merged file for Customer
        /// </summary>
        public static string Customer { get; set; }

        /// <summary>
        /// Merged file for Previus
        /// </summary>
        public static string Previus { get; set; }

        /// <summary>
        /// Merged file for Next
        /// </summary>
        public static string Next { get; set; }

        /// <summary>
        /// The count of nodes (lines)
        /// </summary>
        public static int NodesCount { get; set; }

        #endregion
    }
}
